package com.happy3w.java.ext;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class FileUtils {
    private FileUtils() {

    }

    public static Stream<File> enumerateFilesByPattern(String pattern) {
        int wildcardIndex = pattern.indexOf('*');
        if (wildcardIndex < 0) {
            return Stream.of(new File(pattern));
        }

        int slashIndex = pattern.lastIndexOf('/', wildcardIndex);
        String parentDir = slashIndex >= 0 ? pattern.substring(0, slashIndex) : ".";
        String leftPatten = pattern.substring(slashIndex + 1);

        Stream<File> fileStream = Stream.of(new File(parentDir));
        for (String patternItem : leftPatten.split("/")) {
            fileStream = fileStream.flatMap(file -> enumFileBySinglePattern(file, patternItem));
        }

        return fileStream;
    }

    private static Stream<File> enumFileBySinglePattern(File file, String patternItem) {
        if (patternItem.equals("**")) {
            return StreamSupport.stream(new EnumerateFileSpliterator(file,
                    EnumFileOption.builder()
                            .acceptFile(false)
                            .acceptDir(true)
                            .build()),
                    false);
        } else {
            Pattern fileMatcher = Pattern.compile("^" + patternItem.replaceAll("\\*", "[^/]*") + "$");
            File[] matchedFiles = file.listFiles((dir, name) -> fileMatcher.matcher(name).matches());
            return (matchedFiles == null || matchedFiles.length == 0)
                    ? Stream.empty()
                    : Arrays.asList(matchedFiles).stream();
        }
    }

    public static Stream<File> enumerateFiles(File file) {
        return enumerateFiles(file, EnumFileOption.DEFAULT);
    }

    public static Stream<File> enumerateFiles(File file, EnumFileOption option) {
        if (file.isFile()) {
            return Stream.of(file);
        }

        return StreamSupport.stream(new EnumerateFileSpliterator(file, option), false);
    }

    public static Stream<String> lines(InputStream input) {
        BufferedReader br = new BufferedReader(new InputStreamReader(input));
        try {
            return br.lines().onClose(() -> {
                try {
                    br.close();
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            });
        } catch (Error | RuntimeException e) {
            try {
                br.close();
            } catch (IOException ex) {
                try {
                    e.addSuppressed(ex);
                } catch (Throwable ignore) {
                }
            }
            throw e;
        }
    }

    public static String loadTextFile(Path path, Charset charset) throws IOException {
        byte[] bs = Files.readAllBytes(path);
        if (bs == null) {
            return null;
        }

        if (bs.length == 0) {
            return "";
        }

        if ((charset == null || charset.name().equals("UTF-8"))
                && bs.length >= IgnoreUtf8BomInputStream.BOM.length) {
            byte[] subBs = Arrays.copyOf(bs, IgnoreUtf8BomInputStream.BOM.length);
            if (Arrays.equals(IgnoreUtf8BomInputStream.BOM, subBs)) {
                return new String(bs, IgnoreUtf8BomInputStream.BOM.length, bs.length - IgnoreUtf8BomInputStream.BOM.length, charset);
            }
        }

        return charset == null ? new String(bs) : new String(bs, charset);
    }
}
